<?php

/**
 * Adjustable
 * @author Craig Anderson <craiga@craiga.id.au>
 */


/**
 * Base class for all Adjustable-generated exceptions.
 * Includes a member for the HTTP status line to be sent if this exception bubbles out to the main page.
 * @author Craig Anderson <craiga@craiga.id.au>
 */
abstract class AdjustableException extends Exception
{
	private $_httpStatusLine;
	
	public function __construct($httpStatusLine, $message)
	{
		$this->_httpStatusLine = $httpStatusLine;
		parent::__construct($message);
	}
	
	public function getHttpStatusLine()
	{
		return $this->_httpStatusLine;
	}
}

/**
 * Not featured exception. Analgous to an HTTP 501 Not Implemented status.
 * @author Craig Anderson <craiga@craiga.id.au>
 */
class NotImplementedException extends AdjustableException
{
	public function __construct($feature)
	{
		parent::__construct('HTTP/1.1 501 Not Implemented', sprintf('%s not yet implemented.', $feature));
	}
}

/**
 * Abstract not found exception.
 * @author Craig Anderson <craiga@craiga.id.au>
 */
abstract class NotFoundException extends AdjustableException
{
	public function __construct($message)
	{
		parent::__construct('HTTP/1.1 404 Not Found', $message);
	}
}

/**
 * Script not found exception
 * @author Craig Anderson <craiga@craiga.id.au>
 */
class ScriptNotFoundException extends NotFoundException
{
	public function __construct($scriptIdentifier)
	{
		parent::__construct(sprintf('Script %s not found.', $scriptIdentifier));
	}
}

// set up error reporting and handling
error_reporting(E_ALL | E_STRICT);
function error_handler($errno, $errstr, $errfile, $errline)
{
	// translate errors into ErrorExceptions
	throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler('error_handler');

try
{
	// determine if the request was for a specific script
	if(preg_match('/^[a-z1-9\-\.]+$/i', $_SERVER['QUERY_STRING']) == 1)
	{
		// request was for a specific script
		// get the script identifier
		$scriptIdentifier = $_SERVER['QUERY_STRING'];
		// get info about the script file
		$fileInfo = new SplFileInfo('./adjustable/' . $scriptIdentifier . '.js');
		// test if the script file exists
		if($fileInfo->isFile() && $fileInfo->isReadable())
		{
			// the script file exists
			if(class_exists('HttpResponse'))
			{
				// create the HTTP response to send
				$response = new HttpResponse();
				$response->setCache(true);
				$response->setCacheControl('public');
				$response->setContentDisposition($scriptIdentifier . '.js', true);
				$response->setContentType('text/javascript');
				$response->setFile($fileInfo->getRealPath());
				$response->setGzip(true);
				$response->send();
			}
			else
			{
				// build headers
				$headers = array();
				// equiv. of setCache and setCacheControl?
				$headers[] = 'Cache-Control: public';
				// $response->setContentDisposition($scriptIdentifier . '.js', true);
				$headers[] = sprintf('Content-Disposition: inline; filename=%s.js', $scriptIdentifier);
				// $response->setContentType('text/javascript');
				$headers[] = 'Content-Type: text/javascript';
				// $response->setFile($fileInfo->getRealPath());
				date_default_timezone_set('Australia/Melbourne'); // calling date without first calling date_default_timezone_set can raise a strict warning
				$headers[] = sprintf('Last-Modified: %s', date('r', $fileInfo->getMTime()));
				// open the file
				$file = $fileInfo->openFile();
				// send headers
				foreach($headers as $header)
				{
					header($header);
				}
				// send file contents
				$file->fpassthru();
			}
			
			// get the file
			
		}
		else
		{
			// the script file does not exist
			throw new ScriptNotFoundException($scriptIdentifier);
		}
	}
	else
	{
		// request was for a bundle
		
		$script = file_get_contents('adjustableclient.js');
		
		// build headers
		$headers = array();
		// equiv. of setCache and setCacheControl?
		$headers[] = 'Cache-Control: public';
//		// $response->setContentDisposition($scriptIdentifier . '.js', true);
//		$headers[] = sprintf('Content-Disposition: inline; filename=%s.js', $scriptIdentifier);
		// $response->setContentType('text/javascript');
		$headers[] = 'Content-Type: text/javascript';
//		// $response->setFile($fileInfo->getRealPath());
//		date_default_timezone_set('Australia/Melbourne'); // calling date without first calling date_default_timezone_set can raise a strict warning
//		$headers[] = sprintf('Last-Modified: %s', date('r', $fileInfo->getMTime()));
		// send headers
		foreach($headers as $header)
		{
			header($header);
		}
		// send file contents
		echo($script);
		
		
	}
}
catch(Exception $e)
{
	$statusLine = 'HTTP/1.0 500 Server Error';
	if($e instanceof AdjustableException)
	{
		header($e->getHttpStatusLine());
	}
	header($statusLine);
	
	$errorReportTemplate = <<<ERRORHTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
	<title>Adjustable Error</title>
</head>
<body>
	<table>
		<tr>
			<th scope="row">Exception Class</th>
			<td><pre>%s</pre></td>
		</tr>
		<tr>
			<th scope="row">Message</th>
			<td><pre>%s</pre></td>
		</tr>
		<tr>
			<th scope="row">Code</th>
			<td><pre>%s</pre></td>
		</tr>
		<tr>
			<th scope="row">File</th>
			<td><pre>%s</pre></td>
		</tr>
		<tr>
			<th scope="row">Line</th>
			<td><pre>%s</pre></td>
		</tr>
		<tr>
			<th scope="row">Trace</th>
			<td><pre>%s</pre></td>
		</tr>
	</table>
</body>
</html>
ERRORHTML;
	
	printf($errorReportTemplate, get_class($e),
		htmlspecialchars($e->getMessage()),
		htmlspecialchars($e->getCode()),
		htmlspecialchars($e->getFile()),
		htmlspecialchars($e->getLine()),
		htmlspecialchars($e->getTraceAsString()));
	
}